/*
** __SaM__'s Particle System
** Copyright (C) 2008, Antonenko Artem
**
** shaParticleSystem source file
*/

#include <math.h>
#include "../../include/hgeparticle.h"
#include "../../include/shaparticle.h"
#include "shafile.h"

HGE	*shaParticleSystem::hge = 0;
shaFileReader shaParticleSystem::file;


POINT	neighbours[8] = {{-1, -1}, {1, 1}, {-1, 1}, {1, -1}, {1, 0}, {-1, 0}, {0, 1}, {0, -1}};

#define readBlock(pPar, blockSize) shaParticleSystem::file.read(pPar, blockSize)
#define writeBlock(handle, pPar, blockSize, sizeRead) fwrite(pPar, 1, blockSize, handle)

#define clearParam(p)	(p).n = 0;(p).cur = (p).graph[0].v;										\
						(p).dv =	((p).graph[1].v - (p).graph[0].v)/							\
									((p).graph[1].t - (p).graph[0].t)

#define zeroParam(p)	(p).n = 0;(p).cur = 0;(p).dv = 0;										\
						(p).size = 1;(p).graph[0].v = (p).graph[0].t = 0;						\
						(p).bParticle = false;

#define convert(p, old)	(p).n = 0;																\
						(p).graph[0].v = (old);													\
						(p).graph[0].t = 0;														\
						(p).cur = old;															\
						(p).size = 1

#define updateParam(p)																			\
	if (!p.bParticle)																			\
	{																							\
		if (info.eContinuous && fGraphTime >= info.eLifetime)									\
		{																						\
			p.n = 0;																			\
			p.cur = p.graph[p.n].v;																\
			p.dv = (p.graph[1].v - p.graph[0].v)/(p.graph[1].t - p.graph[0].t);					\
		}																						\
		else if (p.n < p.size - 1 && fGraphTime >= p.graph[p.n + 1].t * info.eLifetime)			\
		{																						\
			p.n++;																				\
			p.cur = p.graph[p.n].v;																\
			p.dv = (p.graph[p.n + 1].v - p.graph[p.n].v)/(p.graph[p.n + 1].t - p.graph[p.n].t);	\
		}																						\
		if (p.n < p.size - 1)																	\
		{																						\
			p.cur += p.dv / info.eLifetime * fDeltaTime;										\
		}																						\
	}

#define updateSysColor(p)																		\
	if (!p.bParticle)																			\
	{																							\
		if (info.eContinuous && fGraphTime >= info.eLifetime)									\
		{																						\
			p.n = 0;																			\
			p.r = (float)GETR((int)p.graph[p.n].v);												\
			p.g = (float)GETG((int)p.graph[p.n].v);												\
			p.b = (float)GETB((int)p.graph[p.n].v);												\
		}																						\
		else if (p.n < p.size - 1 && fGraphTime >= p.graph[p.n + 1].t * info.eLifetime)			\
		{																						\
			p.n++;																				\
			p.r = (float)GETR((int)p.graph[p.n].v);												\
			p.g = (float)GETG((int)p.graph[p.n].v);												\
			p.b = (float)GETB((int)p.graph[p.n].v);												\
		}																						\
		else if (p.n < p.size - 1)																\
		{																						\
			p.r += float(																		\
					(GETR((int)p.graph[p.n + 1].v) - GETR((int)p.graph[p.n].v))/				\
					((p.graph[p.n + 1].t - p.graph[p.n].t) * info.eLifetime)) * fDeltaTime;		\
			p.g += float(																		\
					(GETG((int)p.graph[p.n + 1].v) - GETG((int)p.graph[p.n].v))/				\
					((p.graph[p.n + 1].t - p.graph[p.n].t) * info.eLifetime)) * fDeltaTime;		\
			p.b += float(																		\
					(GETB((int)p.graph[p.n + 1].v) - GETB((int)p.graph[p.n].v))/				\
					((p.graph[p.n + 1].t - p.graph[p.n].t) * info.eLifetime)) * fDeltaTime;		\
		}																						\
	}

void _Graph::read()
{
	DWORD dwSize = 0;

	bParticle = false;
	graph[0].t = graph[0].v = 0;
	n = 0;
	size = 1;

	readBlock(&bParticle, sizeof(bParticle));
	readBlock(&size, sizeof(size));
	readBlock(graph, sizeof(*graph) * size);
}

void _Graph::write(FILE* hF)
{
	DWORD dwSize = 0;
	writeBlock(hF, &bParticle, sizeof(bParticle), dwSize);
	writeBlock(hF, &size, sizeof(size), dwSize);
	writeBlock(hF, graph, sizeof(*graph) * size, dwSize);
}

void shaParticleSystemInfo::write(FILE* hF)
{
	DWORD dwSize = 0;
	BYTE strLen = 0;

	writeBlock(hF, &gBlendmode, sizeof(gBlendmode), dwSize);
	writeBlock(hF, &gFrame, sizeof(gFrame), dwSize);
	writeBlock(hF, &pUnlimited, sizeof(pUnlimited), dwSize);
	writeBlock(hF, &eLifetime, sizeof(eLifetime), dwSize);
	writeBlock(hF, &eContinuous, sizeof(eContinuous), dwSize);
	writeBlock(hF, &ePartLimit, sizeof(ePartLimit), dwSize);
	writeBlock(hF, &eEmitterType, sizeof(eEmitterType), dwSize);
	writeBlock(hF, &eMoveType, sizeof(eMoveType), dwSize);

	pLifeMin.write(hF);
	pLifeMax.write(hF);
	pAddSpeed.write(hF);
	pGravityMin.write(hF);
	pGravityMax.write(hF);
	pAddGravity.write(hF);
	pRadialAccelMin.write(hF);
	pRadialAccelMax.write(hF);
	pAddRadialAccel.write(hF);
	pTangentialAccelMin.write(hF);
	pTangentialAccelMax.write(hF);
	pAddTangentialAccel.write(hF);
	pSize.write(hF);
	pSizeVar.write(hF);
	pSpin.write(hF);
	pSpinVar.write(hF);
	pColor.write(hF);
	pAlpha.write(hF);
	pFPS.write(hF);

	eEmission.write(hF);
	eDirection.write(hF);
	eSpread.write(hF);
	eSpeedMin.write(hF);
	eSpeedMax.write(hF);
	eEmitterW.write(hF);
	eEmitterH.write(hF);
	eMoveSize1.write(hF);
	eMoveSize2.write(hF);
	eMoveAngle.write(hF);
	eMoveSpeed.write(hF);

#ifdef UNICODE
	strLen = (BYTE)wcslen(mask);
	writeBlock(hF, &strLen, sizeof(strLen), dwSize);
	writeBlock(hF, mask, sizeof(wchar_t) * strLen, dwSize);
	strLen =(BYTE)wcslen(path);
    writeBlock(hF, &strLen, sizeof(strLen), dwSize);
	writeBlock(hF, path, sizeof(wchar_t) * strLen, dwSize);
#else
	strLen = (BYTE)strlen(mask);
	writeBlock(hF, &strLen, sizeof(strLen), dwSize);
	writeBlock(hF, mask, sizeof(char) * strLen, dwSize);
	strLen = (BYTE)strlen(path);
    writeBlock(hF, &strLen, sizeof(strLen), dwSize);
	writeBlock(hF, path, sizeof(char) * strLen, dwSize);
#endif

	pSizeProportion.write(hF);
}	

void shaParticleSystemInfo::clear()
{
	clearParam(eDirection);
	clearParam(eEmission);
	clearParam(eEmitterW);
	clearParam(eEmitterH);
	clearParam(eMoveSize1);
	clearParam(eMoveSize2);
	clearParam(eMoveAngle);
	clearParam(eMoveSpeed);
	clearParam(eSpeedMin);
	clearParam(eSpeedMax);
	clearParam(eSpread);
	clearParam(pAddGravity);
	clearParam(pAddRadialAccel);
	clearParam(pAddTangentialAccel);
	clearParam(pAddSpeed);
	clearParam(pAlpha);
	clearParam(pFPS);
	clearParam(pGravityMin);
	clearParam(pGravityMax);
	clearParam(pLifeMin);
	clearParam(pLifeMax);
	clearParam(pRadialAccelMin);
	clearParam(pRadialAccelMax);
	clearParam(pTangentialAccelMin);
	clearParam(pTangentialAccelMax);
	clearParam(pSpin);
	clearParam(pSpinVar);
	clearParam(pSize);
	clearParam(pSizeVar);
	clearParam(pSizeProportion);
	
	pColor.n = 0;
	pColor.r = (float)GETR((int)pColor.graph[0].v);
	pColor.g = (float)GETG((int)pColor.graph[0].v);
	pColor.b = (float)GETB((int)pColor.graph[0].v);
}

void shaParticleSystemInfo::zero()
{
	ZeroMemory(this, sizeof(*this));
	zeroParam(eDirection);
	zeroParam(eEmission);
	zeroParam(eEmitterW);
	zeroParam(eEmitterH);
	zeroParam(eMoveSize1);
	zeroParam(eMoveSize2);
	zeroParam(eMoveAngle);
	zeroParam(eMoveSpeed);
	zeroParam(eSpeedMin);
	zeroParam(eSpeedMax);
	zeroParam(eSpread);
	zeroParam(pAddGravity);
	zeroParam(pAddRadialAccel);
	zeroParam(pAddTangentialAccel);
	zeroParam(pAddSpeed);
	zeroParam(pAlpha);
	zeroParam(pFPS);
	zeroParam(pGravityMin);
	zeroParam(pGravityMax);
	zeroParam(pLifeMin);
	zeroParam(pLifeMax);
	zeroParam(pRadialAccelMin);
	zeroParam(pRadialAccelMax);
	zeroParam(pTangentialAccelMin);
	zeroParam(pTangentialAccelMax);
	zeroParam(pSpin);
	zeroParam(pSpinVar);
	zeroParam(pSize);
	zeroParam(pSizeVar);
	zeroParam(pSizeProportion);

	pColor.r = 
	pColor.g = 
	pColor.b = 0;
	pColor.n = 0;
	pColor.size = 1;
	pColor.bParticle = false;
	pColor.graph[0].t = 
	pColor.graph[0].v = 0;
}	

#ifdef UNICODE
bool shaParticleSystemInfo::read(const wchar_t* file)
#else
bool shaParticleSystemInfo::read(const char* file)
#endif
{
	DWORD dwSize = 0;
	BYTE strLen = 0;
	char sig[7] = {0};

	shaParticleSystem::file.open(file);

	readBlock(sig, 6);
	if (strcmp(sig, PS_SIGNATURE))
	{
		shaParticleSystem::file.close();
		return false;
	}

	readBlock(&gBlendmode, sizeof(gBlendmode));
	readBlock(&gFrame, sizeof(gFrame));
	readBlock(&pUnlimited, sizeof(pUnlimited));
	readBlock(&eLifetime, sizeof(eLifetime));
	readBlock(&eContinuous, sizeof(eContinuous));
	readBlock(&ePartLimit, sizeof(ePartLimit));
	readBlock(&eEmitterType, sizeof(eEmitterType));
	readBlock(&eMoveType, sizeof(eMoveType));

	pLifeMin.read();
	pLifeMax.read();
	pAddSpeed.read();
	pGravityMin.read();
	pGravityMax.read();
	pAddGravity.read();
	pRadialAccelMin.read();
	pRadialAccelMax.read();
	pAddRadialAccel.read();
	pTangentialAccelMin.read();
	pTangentialAccelMax.read();
	pAddTangentialAccel.read();
	pSize.read();
	pSizeVar.read();
	pSpin.read();
	pSpinVar.read();
	pColor.read();
	pAlpha.read();
	pFPS.read();

	eEmission.read();
	eDirection.read();
	eSpread.read();
	eSpeedMin.read();
	eSpeedMax.read();
	eEmitterW.read();
	eEmitterH.read();
	eMoveSize1.read();
	eMoveSize2.read();
	eMoveAngle.read();
	eMoveSpeed.read();

#ifdef UNICODE
	readBlock(&strLen, sizeof(strLen));
	readBlock(mask, sizeof(wchar_t) * strLen);
	mask[strLen] = L'\0';
	readBlock(&strLen, sizeof(strLen));
	readBlock(path, sizeof(wchar_t) * strLen);
	path[strLen] = L'\0';
#else
	readBlock(&strLen, sizeof(strLen));
	readBlock(mask, sizeof(char) * strLen);
	mask[strLen] = '\0';
	readBlock(&strLen, sizeof(strLen));
	readBlock(path, sizeof(char) * strLen);
	path[strLen] = '\0';
#endif
	pSizeProportion.read();

	shaParticleSystem::file.close();
	return true;
}

void shaPSpath::findStart()
{
	int i, a;
	for (i = 0; i < w; i++)
	{
		for (a = 0; a < h; a++)
		{
			if (p(data, i, a) == START)
			{
				sx = x = i;
				sy = y = a;
				return;
			}
		}
	}
}

void shaPSpath::move()
{
	if (!data || !tex)
	{
		return;
	}
	int i = 0, dx = 0, dy = 0;
	bool onlyWay = false;
	static bool jump = false;

	if (jump)
	{
		this->x += this->dx;
		this->y += this->dy;
		jump = false;
		return;
	}

	for (i = 0; i < 8; i++)
	{
		if (GETA(p(data, x + neighbours[i].x, y + neighbours[i].y)) > 0 &&
			(this->dx != -neighbours[i].x || this->dy != -neighbours[i].y) &&
			((GETA(p(data, x + neighbours[i].x, y)) == 0 && GETA(p(data, x, y + neighbours[i].y)) == 0) || i > 3))
		{
			if (p(data, x, y) == PATH &&
				p(data, x + neighbours[i].x, y + neighbours[i].y) == WAY &&
				p(data, x + neighbours[i].x * 2, y + neighbours[i].y * 2) == PATH)
			{
				this->dx = neighbours[i].x;
				this->dy = neighbours[i].y;
				x += this->dx;
				y += this->dy;
				jump = true;
				return;
			}
			if (p(data, x + neighbours[i].x, y + neighbours[i].y) == WAY ||
				p(data, x + neighbours[i].x, y + neighbours[i].y) == START)
			{
				this->dx = neighbours[i].x;
				this->dy = neighbours[i].y;
				x += this->dx;
				y += this->dy;
				return;
			}
			if (p(data, x + neighbours[i].x, y + neighbours[i].y) == PATH && !onlyWay)
			{
				dx = neighbours[i].x;
				dy = neighbours[i].y;
				onlyWay = true;
			}
		}
	}
	this->dx = dx;
	this->dy = dy;
	this->x += dx;
	this->y += dy;
}

#ifdef UNICODE
shaParticleSystem::shaParticleSystem(const wchar_t *filename, hgeAnimation *particle)
#else
shaParticleSystem::shaParticleSystem(const char *filename, hgeAnimation *particle)
#endif
{
	hge = hgeCreate(HGE_VERSION);

	info.zero();
	info.read(filename);
	info.gParticle = new hgeAnimation(*particle);
	info.gParticle->SetFrame(info.gFrame);
	info.gParticle->SetBlendMode(info.gBlendmode);

	vecLocation.x = vecPrevLocation.x = 0.0f;
	vecLocation.y = vecPrevLocation.y = 0.0f;
	fTx = fTy = 0;

	fEmissionResidue = 0.0f;
	nParticlesAlive = 0;
	fGraphTime = -2.0f;
	fScale = 1.0f;
	fAge = 0;

	rectBoundingBox.Clear();
	bUpdateBoundingBox = false;

	clear();

	ZeroMemory(&mask, sizeof(mask));
	ZeroMemory(&s_path, sizeof(s_path));
	BuildMask();
	if (info.eMoveType == MPICTURE)
	{
		BuildPath();
	}
	else if (info.eMoveType == MSPLINE)
	{
		BuildSpline();
	}
}

shaParticleSystem::shaParticleSystem(const shaParticleSystemInfo *sps)
{
	hge = hgeCreate(HGE_VERSION);

	memcpy(&info, sps, sizeof(shaParticleSystemInfo));

	info.gParticle = new hgeAnimation(*info.gParticle);

	vecLocation.x = vecPrevLocation.x = 0.0f;
	vecLocation.y = vecPrevLocation.y = 0.0f;
	fTx = fTy = 0;

	fEmissionResidue = 0.0f;
	nParticlesAlive = 0;
	fGraphTime = -2.0f;
	fScale = 1.0f;
	fAge = 0;

	rectBoundingBox.Clear();
	bUpdateBoundingBox = false;

	clear();
	
	ZeroMemory(&mask, sizeof(mask));
	ZeroMemory(&s_path, sizeof(s_path));
	BuildMask();
	if (info.eMoveType == MPICTURE)
	{
		BuildPath();
	}
	else if (info.eMoveType == MSPLINE)
	{
		BuildSpline();
	}
}

shaParticleSystem::shaParticleSystem(const hgeParticleSystemInfo *psi)
{
	hge = hgeCreate(HGE_VERSION);

	clear();

	info.zero();
	
	convertPSI2SPS(&info, psi);

	info.gParticle = new hgeAnimation(*((hgeAnimation*)psi->sprite));

	vecLocation.x = vecPrevLocation.x = 0.0f;
	vecLocation.y = vecPrevLocation.y = 0.0f;
	fTx = fTy = 0;

	fEmissionResidue = 0.0f;
	nParticlesAlive = 0;
	fGraphTime = -2.0f;
	fScale = 1.0f;
	fAge = 0;

	rectBoundingBox.Clear();
	bUpdateBoundingBox = false;
	
	ZeroMemory(&mask, sizeof(mask));
	ZeroMemory(&s_path, sizeof(s_path));
}

shaParticleSystem::shaParticleSystem(const shaParticleSystem &ps)
{
	memcpy(this, &ps, sizeof(shaParticleSystem));
	hge = hgeCreate(HGE_VERSION);

	info.gParticle = new hgeAnimation(*info.gParticle);

	vecLocation.x = vecPrevLocation.x = 0.0f;
	vecLocation.y = vecPrevLocation.y = 0.0f;
	fTx = fTy = 0;

	fEmissionResidue = 0.0f;
	nParticlesAlive = 0;
	fGraphTime = -2.0f;
	fScale = 1.0f;
	fAge = 0;

	rectBoundingBox.Clear();
	bUpdateBoundingBox = false;

	clear();

	ZeroMemory(&mask, sizeof(mask));
	ZeroMemory(&s_path, sizeof(s_path));
	BuildMask();
	if (info.eMoveType == MPICTURE)
	{
		BuildPath();
	}
	else if (info.eMoveType == MSPLINE)
	{
		BuildSpline();
	}
}

shaParticleSystem::~shaParticleSystem()
{
	if (info.gParticle)
	{
		delete info.gParticle;
	}
    FreeMask();
	FreePath();
	spline.ClearAllPoints();
	hge->Release();
}

shaParticleSystem&	shaParticleSystem::operator= (const shaParticleSystem &ps)
{
	if (info.gParticle)
	{
		delete info.gParticle;
	}

	memcpy(this, &ps, sizeof(shaParticleSystem));

	info.gParticle = new hgeAnimation(*info.gParticle);

	vecLocation.x = vecPrevLocation.x = 0.0f;
	vecLocation.y = vecPrevLocation.y = 0.0f;
	fTx = fTy = 0;

	fEmissionResidue = 0.0f;
	nParticlesAlive = 0;
	fGraphTime = -2.0f;
	fAge = 0;

	rectBoundingBox.Clear();
	bUpdateBoundingBox = false;

	clear();

	ZeroMemory(&mask, sizeof(mask));
	ZeroMemory(&s_path, sizeof(s_path));
	BuildMask();
	if (info.eMoveType == MPICTURE)
	{
		BuildPath();
	}
	else if (info.eMoveType == MSPLINE)
	{
		BuildSpline();
	}

	return *this;
}

void shaParticleSystem::convertPSI2SPS(shaParticleSystemInfo* sps, const hgeParticleSystemInfo* psi)
{
	sps->gBlendmode = BYTE((DWORD)psi->sprite >> 16);
	sps->gFrame = short((DWORD)psi->sprite & 0xFFFF);

	convert(sps->eEmission, psi->nEmission);

	sps->ePartLimit = 500;

	if (psi->fLifetime == -1.0f)
	{
		sps->eContinuous = true;
		sps->eLifetime = 10.0f;
	}
	else
	{
		sps->eContinuous = false;
		sps->eLifetime = psi->fLifetime;
	}

	convert(sps->pLifeMax, psi->fParticleLifeMax);
	convert(sps->pLifeMin, psi->fParticleLifeMin);
	convert(sps->eDirection, psi->fDirection + M_PI_2 * psi->bRelative);
	convert(sps->eSpread, psi->fSpread);
	convert(sps->eSpeedMax, psi->fSpeedMax);
	convert(sps->eSpeedMin, psi->fSpeedMin);
	convert(sps->pGravityMax, psi->fGravityMax);
	convert(sps->pGravityMin, psi->fGravityMin);
	convert(sps->pRadialAccelMax, psi->fRadialAccelMax);
	convert(sps->pRadialAccelMin, psi->fRadialAccelMin);
	convert(sps->pTangentialAccelMax, psi->fTangentialAccelMax);
	convert(sps->pTangentialAccelMin, psi->fTangentialAccelMin);

	sps->pSize.bParticle = true;
	sps->pSize.graph[0].v = psi->fSizeStart;
	sps->pSize.graph[0].t = 0;
	sps->pSize.graph[1].v = psi->fSizeEnd;
	sps->pSize.graph[1].t = 1;
	sps->pSize.size = 2;
	sps->pSize.n = 0;
	sps->pSize.cur = psi->fSizeStart;
	convert(sps->pSizeVar, psi->fSizeVar);

	sps->pSizeProportion.graph[0].v = 1;
	sps->pSizeProportion.graph[0].t = 0;
	sps->pSizeProportion.size = 1;
	sps->pSizeProportion.n = 0;
	sps->pSizeProportion.cur = sps->pSizeProportion.graph[0].v;

	sps->pSpin.bParticle = true;
	sps->pSpin.graph[0].v = psi->fSpinStart;
	sps->pSpin.graph[0].t = 0;
	sps->pSpin.graph[1].v = psi->fSpinEnd;
	sps->pSpin.graph[1].t = 1;
	sps->pSpin.size = 2;
	sps->pSpin.n = 0;
	sps->pSpin.cur = psi->fSpinStart;
	convert(sps->pSpinVar, psi->fSpinVar);

	sps->pColor.bParticle = true;
	sps->pColor.graph[0].v = ARGB(0,(BYTE)psi->colColorStart.r * 255.0f,
									(BYTE)psi->colColorStart.g * 255.0f,
									(BYTE)psi->colColorStart.b * 255.0f);
	sps->pColor.graph[1].v = ARGB(0,(BYTE)psi->colColorEnd.r * 255.0f,
									(BYTE)psi->colColorEnd.g * 255.0f,
									(BYTE)psi->colColorEnd.b * 255.0f);
	sps->pColor.graph[0].t = 0;
	sps->pColor.graph[1].t = 1;
	sps->pColor.size = 2;
	sps->pColor.n = 0;
	sps->pColor.r = psi->colColorStart.r * 255.0f;
	sps->pColor.g = psi->colColorStart.g * 255.0f;
	sps->pColor.b = psi->colColorStart.b * 255.0f;

	sps->pAlpha.bParticle = true;
	sps->pAlpha.graph[0].v = psi->colColorStart.a * 255.0f;
	sps->pAlpha.graph[0].t = 0;
	sps->pAlpha.graph[1].v = psi->colColorEnd.a * 255.0f;
	sps->pAlpha.graph[1].t = 1;
	sps->pAlpha.size = 2;
	sps->pAlpha.n = 0;
	sps->pAlpha.cur = psi->colColorStart.a * 255.0f;

	sps->eEmitterH.size = 1;
	sps->eEmitterH.cur = 0;
	sps->eEmitterH.dv = 0;
	sps->eEmitterH.graph[0].v = 0;
	sps->eEmitterH.graph[0].t = 0;
	sps->eEmitterH.n = 0;

	sps->eEmitterW.size = 1;
	sps->eEmitterW.cur = 0;
	sps->eEmitterW.dv = 0;
	sps->eEmitterW.graph[0].v = 0;
	sps->eEmitterW.graph[0].t = 0;
	sps->eEmitterW.n = 0;

	sps->eEmitterType = RECTANGLE;

	sps->eMoveAngle.size = 1;
	sps->eMoveAngle.cur = 0;
	sps->eMoveAngle.dv = 0;
	sps->eMoveAngle.graph[0].v = 0;
	sps->eMoveAngle.graph[0].t = 0;
	sps->eMoveAngle.n = 0;

	sps->eMoveSize1.size = 1;
	sps->eMoveSize1.cur = 0;
	sps->eMoveSize1.dv = 0;
	sps->eMoveSize1.graph[0].v = 0;
	sps->eMoveSize1.graph[0].t = 0;
	sps->eMoveSize1.n = 0;

	sps->eMoveSize2.size = 1;
	sps->eMoveSize2.cur = 0;
	sps->eMoveSize2.dv = 0;
	sps->eMoveSize2.graph[0].v = 0;
	sps->eMoveSize2.graph[0].t = 0;
	sps->eMoveSize2.n = 0;

	sps->eMoveSpeed.size = 1;
	sps->eMoveSpeed.cur = 0;
	sps->eMoveSpeed.dv = 0;
	sps->eMoveSpeed.graph[0].v = 0;
	sps->eMoveSpeed.graph[0].t = 0;
	sps->eMoveSpeed.n = 0;

	sps->eMoveType = NONE;

	sps->pAddGravity.size = 1;
	sps->pAddGravity.cur = 0;
	sps->pAddGravity.dv = 0;
	sps->pAddGravity.graph[0].v = 0;
	sps->pAddGravity.graph[0].t = 0;
	sps->pAddGravity.n = 0;

	sps->pAddRadialAccel.size = 1;
	sps->pAddRadialAccel.cur = 0;
	sps->pAddRadialAccel.dv = 0;
	sps->pAddRadialAccel.graph[0].v = 0;
	sps->pAddRadialAccel.graph[0].t = 0;
	sps->pAddRadialAccel.n = 0;

	sps->pAddTangentialAccel.size = 1;
	sps->pAddTangentialAccel.cur = 0;
	sps->pAddTangentialAccel.dv = 0;
	sps->pAddTangentialAccel.graph[0].v = 0;
	sps->pAddTangentialAccel.graph[0].t = 0;
	sps->pAddTangentialAccel.n = 0;

	sps->pFPS.size = 1;
	sps->pFPS.cur = 0;
	sps->pFPS.dv = 0;
	sps->pFPS.graph[0].v = 0;
	sps->pFPS.graph[0].t = 0;
	sps->pFPS.n = 0;

	sps->pUnlimited = false;
#ifdef UNICODE
	sps->mask[0] = L'\0';
	sps->path[0] = L'\0';
#else
	sps->mask[0] = '\0';
	sps->path[0] = '\0';
#endif
}

void shaParticleSystem::Render()
{
	shaParticle *par = particles;

	for (int i = 0; i < nParticlesAlive; i++)
	{
		if (info.pFPS.bParticle)
		{
			info.gParticle->SetFrame(par->nFrame);
		}
		info.gParticle->SetColor(	par->colColor.GetHWColor());
		info.gParticle->RenderEx(	par->vecLocation.x * fScale + fTx,
									par->vecLocation.y * fScale + fTy,
									par->fSpin * par->fAge,
									par->fSize * fScale * (float)info.pSizeProportion.cur);
		par++;
	}
}

void shaParticleSystem::FireAt(float x, float y)
{
	Stop();
	MoveTo(x, y);
	Fire();
}

void shaParticleSystem::Fire()
{
	if (info.eLifetime == -1.0f)
	{
		fGraphTime = -1.0f;
	}
	else
	{
		fGraphTime = 0.0f;
	}
	fAge = 0;
	clear();
}

void shaParticleSystem::Stop(bool bKillParticles)
{
	fGraphTime = -2.0f;
	if (bKillParticles) 
	{
		nParticlesAlive = 0;
		rectBoundingBox.Clear();
		clear();
	}
}

void shaParticleSystem::Update(float fDeltaTime)
{
	int i;
	float x = 0, y = 0, sin = 0, cos = 0, ang = 0;
	shaParticle *par = particles;
	hgeVector vecAccel, vecAccel2;

	if (fGraphTime >= 0)
	{
		fGraphTime += fDeltaTime;
		fAge += fDeltaTime;
		if (fGraphTime >= info.eLifetime && !info.eContinuous)
		{
			fGraphTime = -2.0f;
		}
	}
	
	if (fGraphTime != -2.0f)
	{
		updateParam(info.eDirection);
		updateParam(info.eEmission);
		updateParam(info.eEmitterH);
		updateParam(info.eEmitterW);
		updateParam(info.eMoveAngle);
		updateParam(info.eMoveSize1);
		updateParam(info.eMoveSize2);
		updateParam(info.eMoveSpeed);
		updateParam(info.eSpeedMax);
		updateParam(info.eSpeedMin);
		updateParam(info.eSpread);
		updateParam(info.pFPS);
		updateParam(info.pGravityMax);
		updateParam(info.pGravityMin);
		updateParam(info.pLifeMax);
		updateParam(info.pLifeMin);
		updateParam(info.pRadialAccelMax);
		updateParam(info.pRadialAccelMin);
		updateParam(info.pSize);
		updateParam(info.pSpin);
		updateParam(info.pSizeProportion);
		updateParam(info.pTangentialAccelMax);
		updateParam(info.pTangentialAccelMin);
		updateParam(info.pAlpha);
		updateSysColor(info.pColor);
	}
	else if (!nParticlesAlive)
	{
		return;
	}

	if (fGraphTime >= info.eLifetime && info.eContinuous)
	{
		fGraphTime -= info.eLifetime;
	}

	if (bUpdateBoundingBox) 
	{
		rectBoundingBox.Clear();
	}

	switch (info.eMoveType)
	{
	case MCIRCLE:
		x = cosf(angle) * (float)info.eMoveSize1.cur;
		y = sinf(angle) * (float)info.eMoveSize2.cur;

		vecLocation.x -= x;
		vecLocation.y -= y;

		angle += (float)info.eMoveSpeed.cur * fDeltaTime;

		if (angle > M_PI * 2.0f)
		{
			angle -= M_PI * 2.0f;
		}

		x = cosf(angle) * (float)info.eMoveSize1.cur;
		y = sinf(angle) * (float)info.eMoveSize2.cur;

		vecLocation.x += x;
		vecLocation.y += y;
		break;
	case MLINE:
		sin = sinf((float)info.eMoveAngle.cur);
		cos = cosf((float)info.eMoveAngle.cur);

		x = (float)info.eMoveSpeed.cur * fDeltaTime * xMul;
		y = (float)info.eMoveSpeed.cur * fDeltaTime * yMul;

		dx += x;
		dy += y;

		if (dx > (float)info.eMoveSize1.cur)
		{
			xMul = -1;
			dx = (float)info.eMoveSize1.cur;
		}
		else if (dx * -1 > (float)info.eMoveSize1.cur)
		{
			xMul = 1;
			dx = -(float)info.eMoveSize1.cur;
		}
		if (dy > (float)info.eMoveSize1.cur)
		{
			yMul = -1;
			dy = (float)info.eMoveSize1.cur;
		}
		else if (dy * -1 > (float)info.eMoveSize1.cur)
		{
			yMul = 1;
			dy = -(float)info.eMoveSize1.cur;
		}
		
		vecLocation.x += x * cos;
		vecLocation.y += y * sin;
		break;
	case MPICTURE:
		angle += (float)info.eMoveSpeed.cur * fDeltaTime;

		while (angle >= 1)
		{
			s_path.move();

			vecLocation.x += s_path.dx;
			vecLocation.y += s_path.dy;

			angle--;
		}		
		break;
	case MSPLINE:
		FPoint fp = spline.GetPointAt(angle);
		pnt.mX = fp.mX; pnt.mY = fp.mY;
		vecLocation.x -= pnt.mX;
		vecLocation.y -= pnt.mY;

		angle += (float)info.eMoveSpeed.cur * fDeltaTime;

		if (angle > spline.GetArcLength())
		{
			angle = 0;
		}

		FPoint fpt = spline.GetPointAt(angle);
		pnt.mX = fpt.mX; pnt.mY = fpt.mY;
		vecLocation.x += pnt.mX;
		vecLocation.y += pnt.mY;
		break;
	}

	if (!info.pFPS.bParticle)
	{
		info.gParticle->SetSpeed((float)info.pFPS.cur);
		info.gParticle->Update(fDeltaTime);
	}

	//update all alive particles

	for (i = 0; i < nParticlesAlive; i++, par++)
	{
		par->fAge += fDeltaTime;

		if (par->fAge >= par->fTerminalAge && !info.pUnlimited)
		{
			nParticlesAlive--;
			memcpy(par, &particles[nParticlesAlive], sizeof(shaParticle));
			i--;
			continue;
		}
		
		if (info.eDirection.bParticle)
		{
			vecAccel.x = cosf(par->fDirection + par->fSpreadAng);
			vecAccel.y = sinf(par->fDirection + par->fSpreadAng);
			par->vecLocation.x += (par->vecSpeed.x + vecAccel.x * par->fAddSpeed) * fDeltaTime;
			par->vecLocation.y += (par->vecSpeed.y + vecAccel.y * par->fAddSpeed) * fDeltaTime;	

			vecAccel.x = par->vecLocation.x - vecLocation.x;
			vecAccel.y = par->vecLocation.y - vecLocation.y;
			vecAccel.Normalize();
		}
		else
		{
			vecAccel.x = par->vecLocation.x - vecLocation.x;
			vecAccel.y = par->vecLocation.y - vecLocation.y;
			vecAccel.Normalize();
			par->vecLocation.x += (par->vecSpeed.x + vecAccel.x * par->fAddSpeed) * fDeltaTime;
			par->vecLocation.y += (par->vecSpeed.y + vecAccel.y * par->fAddSpeed) * fDeltaTime;
		}
		
		
		vecAccel2.x = vecAccel.x;
		vecAccel2.y = vecAccel.y;
		ang = vecAccel2.x;
		vecAccel2.x = -vecAccel2.y;
		vecAccel2.y = ang;
		vecAccel.x *= par->fStartRadialAccel + par->fAddRadialAccel;
		vecAccel.y *= par->fStartRadialAccel + par->fAddRadialAccel;
		vecAccel2.x *= par->fStartTangentialAccel + par->fAddTangentialAccel;
		vecAccel2.y *= par->fStartTangentialAccel + par->fAddTangentialAccel;
		par->vecAccel.x += (vecAccel.x + vecAccel2.x) * fDeltaTime;
		par->vecAccel.y += (vecAccel.y + vecAccel2.y + par->fStartGravity + par->fAddGravity) * fDeltaTime;
		par->vecLocation.x += par->vecAccel.x * fDeltaTime;
		par->vecLocation.y += par->vecAccel.y * fDeltaTime;

		updateParticle(par, fDeltaTime);

		if(bUpdateBoundingBox)
		{
			rectBoundingBox.Encapsulate(par->vecLocation.x, par->vecLocation.y);
		}
    }

	// generate new particles

	if (fGraphTime != -2.0f)
	{
		float fParticlesNeeded = (float)info.eEmission.cur * fDeltaTime + fEmissionResidue;
		int nParticlesCreated = (unsigned int)fParticlesNeeded;
		fEmissionResidue = fParticlesNeeded - nParticlesCreated;

		par = &particles[nParticlesAlive];
		
		for (i = 0; i < nParticlesCreated; i++)
		{
			if (nParticlesAlive >= info.ePartLimit || nParticlesAlive >= MAX_PARTICLES) 
			{
				break;
			}

			par->fAge = 0.0f;
			par->fTerminalAge = hge->Random_Float((float)info.pLifeMin.cur, (float)info.pLifeMax.cur);
			
			par->vecLocation.x = vecPrevLocation.x + (vecLocation.x - vecPrevLocation.x) * hge->Random_Float(0.0f, 1.0f);
			par->vecLocation.y = vecPrevLocation.y + (vecLocation.y - vecPrevLocation.y) * hge->Random_Float(0.0f, 1.0f);

			par->nFrame = 0;
			par->nFPS = 0;
			if (info.pFPS.bParticle)
			{
				par->fFPS = (float)info.pFPS.graph[0].v;
			}

			switch (info.eEmitterType)
			{
			case CIRCLE:
				ang = hge->Random_Float(0, M_PI * 2.0f);
				
				par->vecLocation.x += cosf(ang) * hge->Random_Float((float)info.eEmitterH.cur, (float)info.eEmitterW.cur);
				par->vecLocation.y += sinf(ang) * hge->Random_Float((float)info.eEmitterH.cur, (float)info.eEmitterW.cur);
				break;
			case RECTANGLE:
				par->vecLocation.x += hge->Random_Float((float)info.eEmitterW.cur/2.0f * -1.0f, (float)info.eEmitterW.cur/2.0f);
				par->vecLocation.y += hge->Random_Float((float)info.eEmitterH.cur/2.0f * -1.0f, (float)info.eEmitterH.cur/2.0f);
				break;
			case MASK:
				if (!mask.tex || !mask.data)
				{
					break;
				}
				do
				{
					x = (float)hge->Random_Int(0, hge->Texture_GetWidth(mask.tex) - 1);
					y = (float)hge->Random_Int(0, hge->Texture_GetHeight(mask.tex) - 1);
				}
				while (GETA(mask.data[(int)x + ((int)y) * hge->Texture_GetWidth(mask.tex)]) == 0);
				
				ang = (float)info.eEmitterH.cur;

				cos = cosf(ang);
				sin = sinf(ang);

				x -= mask.w/2.0f;
				y -= mask.h/2.0f;

				par->vecLocation.x += (x * cos - y * sin) * (float)info.eEmitterW.cur;
				par->vecLocation.y += (x * sin + y * cos) * (float)info.eEmitterW.cur;
				break;
			}
			par->fSpreadAng = hge->Random_Float(0, (float)info.eSpread.cur) - (float)info.eSpread.cur/2.0f -  M_PI_2;
			if (info.eDirection.bParticle)
			{
				par->fDirection = (float)info.eDirection.graph[0].v;
				par->nDirection = 0;
			}
			else
			{
				par->fDirection = (float)info.eDirection.cur;
			}
			ang = par->fDirection + par->fSpreadAng;

			par->vecSpeed.x = cosf(ang);
			par->vecSpeed.y = sinf(ang);
			par->fStartSpeed = hge->Random_Float((float)info.eSpeedMin.cur, (float)info.eSpeedMax.cur);
			par->vecSpeed *= par->fStartSpeed;
			par->vecAccel *= 0;
			par->fAddSpeed = (float)info.pAddSpeed.graph[0].v;
			par->nSpeed = 0;

			par->fStartGravity = hge->Random_Float((float)info.pGravityMin.cur, (float)info.pGravityMax.cur);
			par->fStartRadialAccel = hge->Random_Float((float)info.pRadialAccelMin.cur, (float)info.pRadialAccelMax.cur);
			par->fStartTangentialAccel = hge->Random_Float((float)info.pTangentialAccelMin.cur, (float)info.pTangentialAccelMax.cur);

			par->nGravity = 0;
			par->nRadialAccel = 0;
			par->nTangentialAccel = 0;
			par->fAddGravity = (float)info.pAddGravity.graph[0].v;
			par->fAddRadialAccel = (float)info.pAddRadialAccel.graph[0].v;
			par->fAddTangentialAccel = (float)info.pAddTangentialAccel.graph[0].v;
		
			if (info.pSize.bParticle)
			{
				par->nSize = 0;
				if (info.pSize.size >= 2)
				{
					par->fSize = par->fStartSize = hge->Random_Float((float)info.pSize.graph[0].v,
							float(info.pSize.graph[0].v + (info.pSize.graph[1].v - info.pSize.graph[0].v) * info.pSizeVar.cur));
				}
				else
				{
					par->fStartSize = par->fSize = (float)info.pSize.graph[0].v;	
				}
			}
			else
			{
				if (info.pSize.n + 1 < info.pSize.size)
				{
					par->fSize = hge->Random_Float((float)info.pSize.cur,
						float(info.pSize.cur + (info.pSize.graph[info.pSize.n + 1].v - info.pSize.cur) * info.pSizeVar.cur));
				}
				else
				{
					par->fSize = (float)info.pSize.cur;
				}	
			}
			
			if (info.pSpin.bParticle)
			{
				par->nSpin = 0;
				if (info.pSpin.size >= 2)
				{
					par->fSpin = par->fStartSpin = hge->Random_Float((float)info.pSpin.graph[0].v,
							float(info.pSpin.graph[0].v + (info.pSpin.graph[1].v - info.pSpin.graph[0].v) * info.pSpinVar.cur));
				}
				else
				{
					par->fStartSpin = par->fSpin = (float)info.pSpin.graph[0].v;	
				}
			}
			else
			{
				if (info.pSpin.n + 1 < info.pSpin.size)
				{
					par->fSpin = hge->Random_Float((float)info.pSpin.cur,
						float(info.pSpin.cur + (info.pSpin.graph[info.pSpin.n + 1].v - info.pSpin.cur) * info.pSpinVar.cur));
				}
				else
				{
					par->fSpin = (float)info.pSpin.cur;
				}	
			}
            
			if (info.pColor.bParticle)
			{
				par->nColor = 0;
				par->colColor = ARGB(0,
									GETR((int)info.pColor.graph[0].v),
									GETG((int)info.pColor.graph[0].v),
									GETB((int)info.pColor.graph[0].v));
				par->r = par->colColor.r * 255.0f;
				par->g = par->colColor.g * 255.0f;
				par->b = par->colColor.b * 255.0f;
				par->a = 0;	
			}
			else
			{
				par->colColor = ARGB(0, (int)info.pColor.r, (int)info.pColor.g, (int)info.pColor.b);
			}

			if (info.pAlpha.bParticle)
			{
				par->nAlpha = 0;
				par->colColor.a = (float)info.pAlpha.graph[0].v/255.0f;
				par->a = (float)info.pAlpha.graph[0].v;	
			}
			else
			{
				par->colColor.a = (float)info.pAlpha.cur/255.0f;
			}

			if (bUpdateBoundingBox) 
			{
				rectBoundingBox.Encapsulate(par->vecLocation.x, par->vecLocation.y);
			}

			nParticlesAlive++;
			par++;
		}
	}

	vecPrevLocation = vecLocation;
}

void shaParticleSystem::MoveTo(float x, float y, bool bMoveParticles)
{
	float dx, dy;

	if (bMoveParticles)
	{
		dx = x - vecLocation.x;
		dy = y - vecLocation.y;

		for (int i = 0;i < nParticlesAlive; i++)
		{
			particles[i].vecLocation.x += dx;
			particles[i].vecLocation.y += dy;
		}

		vecPrevLocation.x = vecPrevLocation.x + dx;
		vecPrevLocation.y = vecPrevLocation.y + dy;
	}
	else
	{
		if (fGraphTime == -2.0)
		{ 
			vecPrevLocation.x = x;
			vecPrevLocation.y = y;
		}
		else 
		{ 
			vecPrevLocation.x = vecLocation.x;
			vecPrevLocation.y = vecLocation.y;
		}
	}

	vecLocation.x = x;
	vecLocation.y = y;
	
	switch (info.eMoveType)
	{
	case MCIRCLE:
		vecLocation.x += cosf(angle) * (float)info.eMoveSize1.cur;
		vecLocation.y += sinf(angle) * (float)info.eMoveSize2.cur;
		break;
	case MLINE:
		vecLocation.x += shaParticleSystem::dx;
		vecLocation.y += shaParticleSystem::dy;
		break;
	case MPICTURE:
		vecLocation.x += s_path.x - s_path.sx;
		vecLocation.y += s_path.y - s_path.sy;
		break;
	case MSPLINE:
		FPoint fp = spline.GetPointAt(angle);
		pnt.mX = fp.mX; pnt.mY = fp.mY;
		vecLocation.x += pnt.mX;
		vecLocation.y += pnt.mY;
		break;
	}
}

void shaParticleSystem::Transpose(float x, float y)
{
	fTx = x;
	fTy = y;
}
void shaParticleSystem::TrackBoundingBox(bool bTrack) 
{ 
	bUpdateBoundingBox = bTrack; 
}

void shaParticleSystem::SetScale(float scale)
{
	if (fScale >= 0.0f)
	{
		fScale = scale;
	}
}

int shaParticleSystem::GetParticlesAlive() const 
{
	return nParticlesAlive;
}

float shaParticleSystem::GetAge() const 
{
	return fAge; 
}

float shaParticleSystem::GetGraphTime() const
{
	return fGraphTime;
}

void shaParticleSystem::GetPosition(float *x, float *y) const 
{ 
	*x = vecLocation.x;
	*y = vecLocation.y;
}

void shaParticleSystem::GetTransposition(float *x, float *y) const 
{ 
	*x = fTx;
	*y = fTy; 
}

hgeRect* shaParticleSystem::GetBoundingBox(hgeRect *rect) const 
{ 
	memcpy(rect, &rectBoundingBox, sizeof(hgeRect));
	return rect;
}

float shaParticleSystem::GetScale() const
{
	return fScale;
}

void shaParticleSystem::BuildMask()
{
	FreeMask();
	if (info.mask[0])
	{
		mask.tex = hge->Texture_Load(info.mask);
		if (mask.tex)
		{
			mask.w = hge->Texture_GetWidth(mask.tex, true);
			mask.h = hge->Texture_GetHeight(mask.tex, true);
			mask.data = hge->Texture_Lock(mask.tex);
			hge->Texture_Unlock(mask.tex);
		}
	}
	else
	{
		ZeroMemory(&mask, sizeof(mask));
	}
}

void shaParticleSystem::BuildPath()
{
	
	if (info.path[0] && info.eMoveType == MPICTURE)
	{
		FreePath();
		s_path.tex = hge->Texture_Load(info.path);
		if (s_path.tex)
		{
			s_path.w = hge->Texture_GetWidth(s_path.tex);
			s_path.h = hge->Texture_GetHeight(s_path.tex);
			s_path.data = hge->Texture_Lock(s_path.tex);
			hge->Texture_Unlock(s_path.tex);
			s_path.findStart();
		}
	}
	else
	{
		ZeroMemory(&s_path, sizeof(s_path));
	}
}

void shaParticleSystem::BuildSpline()
{
	if (info.path[0] && info.eMoveType == MSPLINE)
	{
		FreePath();

		IPoint	p;
		int		gran;
		bool	closed;
		float	hsX, hsY;
		
		if (!file.open(info.path))
		{
			return;
		}
        
		spline.ClearAllPoints();

		readBlock(&gran, sizeof(int));
		spline.SetGranularity(gran);
		readBlock(&closed, 1);
		spline.SetClosed(closed);
		readBlock(&hsX, sizeof(float));
		readBlock(&hsY, sizeof(float));
		
		while (true)
		{
			readBlock(&p, sizeof(IPoint));
			if (!file.isEOF())
			{
				p.mX -= (int)hsX;
				p.mY -= (int)hsY;
				spline.AddPoint(p);
			}
			else
			{
				break;
			}
		}
		spline.RegenerateSplines();
		file.close();
	}
	else
	{
		spline.ClearAllPoints();
	}
}

void shaParticleSystem::RenderMask()
{
	hgeSprite s(mask.tex, 0, 0, (float)hge->Texture_GetWidth(mask.tex, true), (float)hge->Texture_GetHeight(mask.tex, true));
	s.SetHotSpot((float)hge->Texture_GetWidth(mask.tex, true)/2.0f, (float)hge->Texture_GetHeight(mask.tex, true)/2.0f);
	s.RenderEx(vecLocation.x, vecLocation.y, (float)info.eEmitterH.cur, (float)info.eEmitterW.cur);
}

void shaParticleSystem::FreeMask()
{
	if (mask.tex)
	{
		mask.data = 0;
		hge->Texture_Free(mask.tex);
	}
}

void shaParticleSystem::FreePath()
{
	if (s_path.tex)
	{
		hge->Texture_Free(s_path.tex);
		ZeroMemory(&s_path, sizeof(s_path));
	}
}

void shaParticleSystem::updateParticle(shaParticle* p, float dt)
{
	if (info.pFPS.bParticle)
	{
		if (p->nFPS < info.pFPS.size - 1 && p->fAge >= info.pFPS.graph[p->nFPS + 1].t * p->fTerminalAge)
		{
			p->nFPS++;
			p->fFPS = (float)info.pFPS.graph[p->nFPS].v;
		}
		else if (p->nFPS < info.pFPS.size - 1)
		{
			p->fFPS += float(
						(info.pFPS.graph[p->nFPS + 1].v - info.pFPS.graph[p->nFPS].v)/ 
						((info.pFPS.graph[p->nFPS + 1].t - info.pFPS.graph[p->nFPS].t) * p->fTerminalAge)) * dt;
		}
		if (p->fFPS > 0.0f)
		{
			info.gParticle->SetFrame(0);
			info.gParticle->SetSpeed(p->fFPS);
			info.gParticle->Rewind(p->fAge);
			p->nFrame = info.gParticle->GetFrame();
		}
	}
	if (info.pColor.bParticle)
	{
		if (p->nColor < info.pColor.size - 1 && p->fAge >= info.pColor.graph[p->nColor + 1].t * p->fTerminalAge)
		{
			p->nColor++;
			p->r = (float)GETR((int)info.pColor.graph[p->nColor].v);
			p->g = (float)GETG((int)info.pColor.graph[p->nColor].v);
			p->b = (float)GETB((int)info.pColor.graph[p->nColor].v);
			p->colColor.r = p->r/255.0f;
			p->colColor.g = p->g/255.0f;
			p->colColor.b = p->b/255.0f;
		}
		else if (p->nColor < info.pColor.size - 1)
		{
			p->r += float(
					(GETR((int)info.pColor.graph[p->nColor + 1].v) - GETR((int)info.pColor.graph[p->nColor].v))/ 
					((info.pColor.graph[p->nColor + 1].t - info.pColor.graph[p->nColor].t) * p->fTerminalAge)) * dt;
			p->g += float(
					(GETG((int)info.pColor.graph[p->nColor + 1].v) - GETG((int)info.pColor.graph[p->nColor].v))/ 
					((info.pColor.graph[p->nColor + 1].t - info.pColor.graph[p->nColor].t) * p->fTerminalAge)) * dt;
			p->b += float(
					(GETB((int)info.pColor.graph[p->nColor + 1].v) - GETB((int)info.pColor.graph[p->nColor].v))/ 
					((info.pColor.graph[p->nColor + 1].t - info.pColor.graph[p->nColor].t) * p->fTerminalAge)) * dt;
			p->colColor.r = p->r/255.0f;
			p->colColor.g = p->g/255.0f;
			p->colColor.b = p->b/255.0f;
		}
	}

	if (info.pAlpha.bParticle)
	{
		if (p->nAlpha < info.pAlpha.size - 1 && p->fAge >= info.pAlpha.graph[p->nAlpha + 1].t * p->fTerminalAge)
		{
			p->nAlpha++;
			p->a = (float)info.pAlpha.graph[p->nAlpha].v;
			p->colColor.a = p->a/255.0f;
		}
		else if (p->nAlpha < info.pAlpha.size - 1)
		{
			p->a += float((info.pAlpha.graph[p->nAlpha + 1].v - info.pAlpha.graph[p->nAlpha].v)/ 
					((info.pAlpha.graph[p->nAlpha + 1].t - info.pAlpha.graph[p->nAlpha].t) * p->fTerminalAge)) * dt;
			p->colColor.a = p->a/255.0f;
		}
	}

	if (info.pSpin.bParticle)
	{
		if (p->nSpin < info.pSpin.size - 1 && p->fAge >= info.pSpin.graph[p->nSpin + 1].t * p->fTerminalAge)
		{
			p->nSpin++;
			p->fSpin = (float)info.pSpin.graph[p->nSpin].v;
		}
		else if (p->nSpin < info.pSpin.size - 1)
		{
			if (p->nSpin)
			{
				p->fSpin += float((info.pSpin.graph[p->nSpin + 1].v - info.pSpin.graph[p->nSpin].v)/ 
							((info.pSpin.graph[p->nSpin + 1].t - info.pSpin.graph[p->nSpin].t) * p->fTerminalAge)) * dt;
			}
			else
			{
				p->fSpin +=  float((info.pSpin.graph[p->nSpin + 1].v - p->fStartSpin)/ 
							((info.pSpin.graph[p->nSpin + 1].t - info.pSpin.graph[p->nSpin].t) * p->fTerminalAge)) * dt;
			}
		}
	}

	if (info.pSize.bParticle)
	{
		if (p->nSize < info.pSize.size - 1 && p->fAge >= info.pSize.graph[p->nSize + 1].t * p->fTerminalAge)
		{
			p->nSize++;
			p->fSize = (float)info.pSize.graph[p->nSize].v;
		}
		else if (p->nSize < info.pSize.size - 1)
		{
			if (p->nSize)
			{
				p->fSize += float((info.pSize.graph[p->nSize + 1].v - info.pSize.graph[p->nSize].v)/ 
							((info.pSize.graph[p->nSize + 1].t - info.pSize.graph[p->nSize].t) * p->fTerminalAge)) * dt;
			}
			else
			{
				p->fSize += float((info.pSize.graph[p->nSize + 1].v - p->fStartSize)/ 
							((info.pSize.graph[p->nSize + 1].t - info.pSize.graph[p->nSize].t) * p->fTerminalAge)) * dt;
			}
		}
	}

	if (info.eDirection.bParticle)
	{
		if (p->nDirection < info.eDirection.size - 1 && p->fAge >= info.eDirection.graph[p->nDirection + 1].t * p->fTerminalAge)
		{
			p->nDirection++;
			p->fDirection = (float)info.eDirection.graph[p->nDirection].v;
		}
		else if (p->nDirection < info.eDirection.size - 1)
		{
			p->fDirection += float((info.eDirection.graph[p->nDirection + 1].v - info.eDirection.graph[p->nDirection].v)/ 
					((info.eDirection.graph[p->nDirection + 1].t - info.eDirection.graph[p->nDirection].t) * p->fTerminalAge)) * dt;
		}
	}

	if (p->nSpeed < info.pAddSpeed.size - 1 && p->fAge >= info.pAddSpeed.graph[p->nSpeed + 1].t * p->fTerminalAge)
	{
		p->nSpeed++;
		p->fAddSpeed = (float)info.pAddSpeed.graph[p->nSpeed].v;
	}
	else if (p->nSpeed < info.pAddSpeed.size - 1)
	{
		p->fAddSpeed += float((info.pAddSpeed.graph[p->nSpeed + 1].v - info.pAddSpeed.graph[p->nSpeed].v)/ 
				((info.pAddSpeed.graph[p->nSpeed + 1].t - info.pAddSpeed.graph[p->nSpeed].t) * p->fTerminalAge)) * dt;
	}
		
	if (p->nGravity < info.pAddGravity.size - 1 && p->fAge >= info.pAddGravity.graph[p->nGravity + 1].t * p->fTerminalAge)
	{
		p->nGravity++;
		p->fAddGravity = (float)info.pAddGravity.graph[p->nGravity].v;
	}
	else if (p->nGravity < info.pAddGravity.size - 1)
	{
		p->fAddGravity += float((info.pAddGravity.graph[p->nGravity + 1].v - info.pAddGravity.graph[p->nGravity].v)/ 
				((info.pAddGravity.graph[p->nGravity + 1].t - info.pAddGravity.graph[p->nGravity].t) * p->fTerminalAge)) * dt;
	}

	if (p->nRadialAccel < info.pAddRadialAccel.size - 1 && p->fAge >= info.pAddRadialAccel.graph[p->nRadialAccel + 1].t * p->fTerminalAge)
	{
		p->nRadialAccel++;
		p->fAddRadialAccel = (float)info.pAddRadialAccel.graph[p->nRadialAccel].v;
	}
	else if (p->nRadialAccel < info.pAddRadialAccel.size - 1)
	{
		p->fAddRadialAccel += float((info.pAddRadialAccel.graph[p->nRadialAccel + 1].v - info.pAddRadialAccel.graph[p->nRadialAccel].v)/ 
				((info.pAddRadialAccel.graph[p->nRadialAccel + 1].t - info.pAddRadialAccel.graph[p->nRadialAccel].t) * p->fTerminalAge)) * dt;
	}
	
	if (p->nTangentialAccel < info.pAddTangentialAccel.size - 1 && p->fAge >= info.pAddTangentialAccel.graph[p->nTangentialAccel + 1].t * p->fTerminalAge)
	{
		p->nTangentialAccel++;
		p->fAddTangentialAccel = (float)info.pAddTangentialAccel.graph[p->nTangentialAccel].v;
	}
	else if (p->nTangentialAccel < info.pAddTangentialAccel.size - 1)
	{
		p->fAddTangentialAccel += float((info.pAddTangentialAccel.graph[p->nTangentialAccel + 1].v - info.pAddTangentialAccel.graph[p->nTangentialAccel].v)/ 
				((info.pAddTangentialAccel.graph[p->nTangentialAccel + 1].t - info.pAddTangentialAccel.graph[p->nTangentialAccel].t) * p->fTerminalAge)) * dt;
	}
}

void shaParticleSystem::clear()
{
	ZeroMemory(particles, sizeof(shaParticle) * MAX_PARTICLES);
	
	info.clear();
	
	dx = dy = angle = 0;
	xMul = yMul = 1; 
	s_path.dx = 0;
	s_path.dy = 0;
	s_path.findStart();
}
